/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package com.inspur.edp.lcm.metadata.webapi.task;

import com.google.common.collect.Lists;
import com.inspur.edp.lcm.metadata.api.entity.DbConnectionInfo;
import com.inspur.edp.lcm.metadata.api.i18n.LcmMetadataDevLog;
import com.inspur.edp.lcm.metadata.api.i18n.LogCodes;
import com.inspur.edp.lcm.metadata.api.service.MdpkgService;
import com.inspur.edp.lcm.metadata.api.service.ProjectExtendService;
import com.inspur.edp.lcm.metadata.common.StageExecutorService;
import com.inspur.edp.lcm.metadata.common.Utils;
import com.inspur.edp.lcm.metadata.core.thread.BoGraphThreadLocalHolder;
import com.inspur.edp.lcm.metadata.core.thread.DeployTask;
import com.inspur.edp.lcm.metadata.inner.api.data.PublishStatus;
import com.inspur.edp.lcm.metadata.inner.api.enm.StageEnum;
import com.inspur.edp.lcm.metadata.inner.api.utils.IdeLogUtils;
import io.iec.edp.caf.boot.context.CAFContext;
import io.iec.edp.caf.commons.dataaccess.DbType;
import io.iec.edp.caf.core.session.CafSession;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import lombok.extern.slf4j.Slf4j;

/**
 * 发布服务任务
 */
@Slf4j
public class PublishTask implements Runnable {
    private String requestId;
    private String path;
    private List<String> projPaths;
    private MdpkgService mdpkgService;
    private ProjectExtendService projectExtendService;
    private CafSession session;

    private PublishTask(String requestId, MdpkgService mdpkgService, ProjectExtendService projectExtendService,
        String path, List<String> projPaths, CafSession session) {
        this.requestId = requestId;
        this.mdpkgService = mdpkgService;
        this.projectExtendService = projectExtendService;
        this.path = path;
        this.projPaths = projPaths;
        this.session = session;
    }

    @Override
    public void run() {
        try {
            CAFContext.current.getService().setCurrentThreadPoolSession(session);
            BoGraphThreadLocalHolder.init();
            IdeLogUtils.setRequestId(requestId);
            IdeLogUtils.setStageStatus(IdeLogUtils.SUCCESS);
            // 批量生成代码、maven编译
            List<String> operationList = Lists.newArrayList(Utils.GENERATE, Utils.COMPILE);
            mdpkgService.batchOperate(path, operationList);
            if (!IdeLogUtils.SUCCESS.equals(IdeLogUtils.getStageStatus())) {
                return;
            }
            // 批量执行扩展编译
            projectExtendService.batchCompile(path, "", Utils.JITTODLL);
            if (!IdeLogUtils.SUCCESS.equals(IdeLogUtils.getStageStatus())) {
                return;
            }
            // 批量提取
            projectExtendService.extract(path);
            if (!IdeLogUtils.SUCCESS.equals(IdeLogUtils.getStageStatus())) {
                return;
            }
            // 批量部署
            CountDownLatch latch = new CountDownLatch(projPaths.size());
            List<CompletableFuture<Boolean>> deployResultList = new ArrayList<>();
            for (String projPath : projPaths) {
                DbConnectionInfo dbConnectionInfo = new DbConnectionInfo(DbType.PgSQL);
                // 异步执行提取过程
                CompletableFuture<Boolean> cf = CompletableFuture.supplyAsync(DeployTask.ofDeployTask(IdeLogUtils.getRequestId(), dbConnectionInfo, latch, projectExtendService, projPath,this.session), StageExecutorService.getStageExecutorService())
                    // 非阻塞同步回调方法
                    .thenApply((re) -> {
                        IdeLogUtils.syncProcess(PublishStatus.success(IdeLogUtils.calcProcess(projPaths.size(), (int) (projPaths.size() - latch.getCount()), StageEnum.DEPLOY), StageEnum.DEPLOY.getCode()));
                        return true;
                    });
                deployResultList.add(cf);
            }
            // 等待所有成功结束
            CompletableFuture<Void> cfAll = CompletableFuture.allOf(deployResultList.toArray(new CompletableFuture[0]));
            try {
                cfAll.get();
            } catch (Exception e) {
                log.error("Deploy error: ", e);
                IdeLogUtils.syncProcess(PublishStatus.fail(LcmMetadataDevLog.getInstance().getString(LogCodes.LOG_METADATA_DEV_0001, e.getMessage())));
                return;
            }

            Thread.sleep(Utils.PUBLISH_WAIT_TIME);
            publishSucess();
        } catch (InterruptedException e) {
            log.warn("Publish interruptedException: ", e);
            publishSucess();
        } catch (Exception e) {
            log.error("Publish error: ", e);
            IdeLogUtils.syncProcess(PublishStatus.fail(LcmMetadataDevLog.getInstance().getString(LogCodes.LOG_METADATA_DEV_0002, e.getMessage())));
        } catch (Throwable e) {
            // 如果出现缺少类等Error，会造成前端卡住，这边拦截到抛出。
            log.error("Publish error: ", e);
            IdeLogUtils.syncProcess(PublishStatus.fail(LcmMetadataDevLog.getInstance().getString(LogCodes.LOG_METADATA_DEV_0003)));
        } finally {
            IdeLogUtils.removeRequestId();
            IdeLogUtils.removeStageStatus();
            IdeLogUtils.removeRestart();
            BoGraphThreadLocalHolder.clear();
            CAFContext.current.getService().clearCurrentThreadPoolSession();
        }

    }

    public static PublishTask ofPublishTask(String requestId, MdpkgService mdpkgService,
        ProjectExtendService projectExtendService,
        String path, List<String> projPaths, CafSession session) {
        return new PublishTask(requestId, mdpkgService, projectExtendService, path, projPaths, session);
    }

    private void publishSucess() {
        // 发布结束
        IdeLogUtils.syncProcess(
            PublishStatus.success(100, StageEnum.DEPLOY.getCode())
        );
    }
}
